{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第十一讲 字典"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在本讲中，你将学习让你能够将相关信息关联起来的 Python 字典，以及如何访问和修改字典中的信息。字典可存储的信息量几乎不受限制，因此我们会演示如何遍历字典中的数据。另外，你还将学习如何存储字典的列表、列表的字典和字典的字典。\n",
    "\n",
    "理解字典后，你就能够更准确地为各种真实物体建模。你可以创建一个表示人的字典，然后在其中存储你想存储的任何信息：姓名、年龄、地址，以及可以描述这个人的任何其他方面。你还能够在字典中存储任意两种相关的信息，如一系列单词及其含义、一系列人名及其喜欢的数、一系列山脉及其海拔等。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1 一个简单的字典"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "来看一个包含外星人的游戏，这些外星人的颜色和分数各不相同。下面是一个简单的字典，存储了有关特定外星人的信息："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alien.py\n",
    "\n",
    "alien_0 = {'color': 'green', 'points': 5}\n",
    "\n",
    "print(alien_0['color'])\n",
    "print(alien_0['points'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "字典 `alien_0` 存储了这个外星人的颜色和分数。最后两行代码访问并显示这些信息，结果如下：\n",
    "```\n",
    "green\n",
    "5\n",
    "```\n",
    "与大多数编程概念一样，要熟练地使用字典，需要一段时间的练习。使用字典一段时间之后，你就会明白它们为何能够高效地模拟现实世界中的情形。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 使用字典"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Python 中，**字典 (dictionary)** 是一系列**键值对**。每个键都与一个值关联，可以使⽤键来访问与之关联的值。与键关联的值可以是数、字符串、列表乃至字典。事实上，可将任意 Python 对象用作字典中的值。\n",
    "\n",
    "在 Python 中，字典用放在花括号 (`{}`) 中的一系列键值对表示，如前面的示例所示：\n",
    "```python\n",
    "alien_0 = {'color': 'green', 'points': 5}\n",
    "```\n",
    "键值对包含两个相互关联的值。当你指定键时，Python 将返回与之关联的值。键和值之间用冒号分隔，而键值对之间用逗号分隔。在字典中，你想存储多少个键值对都可以。\n",
    "\n",
    "最简单的字典只有一个键值对，如下述修改后的字典 `alien_0` 所示：\n",
    "```python\n",
    "alien_0 = {'color': 'green'}\n",
    "```\n",
    "这个字典只存储了一项有关 `alien_0` 的信息，具体地说是这个外星人的颜色。在这个字典中，字符串 `'color'` 是一个键，与之关联的值为 `'green'`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 访问字典中的值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要获取与键关联的值，可指定字典名并把键放在后面的方括号内，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alien.py\n",
    "alien_0 = {'color': 'green'}\n",
    "\n",
    "print(alien_0['color'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这将返回字典 `alien_0` 中与键 `'color'` 关联的值：\n",
    "```\n",
    "green\n",
    "```\n",
    "字典中可包含任意数量的键值对。例如，最初的字典 `alien_0` 就包含两个键值对：\n",
    "```python\n",
    "alien_0 = {'color': 'green', 'points': 5}\n",
    "```\n",
    "现在，你可以访问外星人 `alien_0` 的颜色和分数。如果玩家消灭了这个外星人，就可以使用下面的代码来确定玩家应获得多少分："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alien_0 = {'color': 'green', 'points': 5}\n",
    "\n",
    "new_points = alien_0['points']\n",
    "print(f\"You just earned {new_points} points!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述代码首先定义了一个字典。然后，从这个字典中获取与键 `'points'` 关联的值，并将这个值赋给变量 `new_points`。最后一行打印一条消息，指出玩家获得了多少分：\n",
    "```\n",
    "You just earned 5 points!\n",
    "```\n",
    "如果在消灭外星人时运行这段代码，就将获取该外星人的分数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 添加键值对"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "字典是一种动态结构，可随时在其中添加键值对。要添加键值对，可依次指定字典名、用方括号括起来的键和与该键关联的值。\n",
    "\n",
    "下面来在字典 `alien_0` 中添加两项信息：外星人的 x 坐标和 y 坐标，以便在屏幕的特定位置上显示该外星人。我们将这个外星人放在屏幕左边缘上，距离屏幕上边缘 25 像素。由于屏幕坐标系的原点通常在左上角，因此要将该外星人放在屏幕左边缘，可将 x 坐标设置为 0；要将该外星人放在距离屏幕上边缘 25 像素的地方，可将 y 坐标设置为 25，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alien.py\n",
    "alien_0 = {'color': 'green', 'points': 5}\n",
    "print(alien_0)\n",
    "\n",
    "alien_0['x_position'] = 0\n",
    "alien_0['y_position'] = 25\n",
    "print(alien_0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先定义前面一直在使用的字典，然后打印这个字典，以显示其信息快照。接下来，在这个字典中新增一个键值对，其中的键为 `'x_position'`、值为 `0`。然后重复同样的操作，但使用的键为 `'y_position'`、值为 `25`。打印修改后的字典，将看到这两个新增的键值对：\n",
    "```\n",
    "{'color': 'green', 'points': 5}\n",
    "{'color': 'green', 'points': 5, 'x_position': 0, 'y_position': 25}\n",
    "```\n",
    "这个字典的最终版本包含 4 个键值对，其中原来的两个指定外星人的颜色和分数，而新增的两个指定其位置。\n",
    "\n",
    "字典会保留定义元素的排列顺序。如果将字典打印出来或遍历其元素，将发现元素的排列顺序与添加顺序相同。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 从一个空字典开始"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有时候，在空字典中添加键值对很方便，甚至是的。为此，可先使用一对空花括号定义一个空字典，再分行添加各个键值对。例如，下面演示了如何以这种方式创建字典 `alien_0`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alien.py\n",
    "alien_0 = {}\n",
    "\n",
    "alien_0['color'] = 'green'\n",
    "alien_0['points'] = 5\n",
    "\n",
    "print(alien_0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先定义空字典 `alien_0`，再在其中添加颜色和分数，得到前述示例一直在使用的字典：\n",
    "```\n",
    "{'color': 'green', 'points': 5}\n",
    "```\n",
    "如果要使用字典来存储用户提供的数据或者编写能自动生成大量键值对的代码，通常都需要先定义一个空字典。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 修改字典中的值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要修改字典中的值，可依次指定字典名、用方括号括起来的键和与该键关联的新值。假设随着游戏的进行，需要将一个外星人从绿色改为黄色："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alien.py\n",
    "alien_0 = {'color': 'green'}\n",
    "print(f\"The alien is {alien_0['color']}.\")\n",
    "\n",
    "alien_0['color'] = 'yellow'\n",
    "print(f\"The alien is now {alien_0['color']}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先定义一个表示外星人 `alien_0` 的字典，其中只包含这个外星人的颜色。接下来，将与键 `'color'` 关联的值改为 `'yellow'`。输出表明，这个外星人确实从绿色变成了黄色：\n",
    "```\n",
    "The alien is green.\n",
    "The alien is now yellow.\n",
    "```\n",
    "来看一个更有趣的例子：对一个能够以不同速度移动的外星人进行位置跟踪。为此，存储该外星人的当前速度，并据此确定该外星人该向右移动多远："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alien_0 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}\n",
    "print(f\"Original position: {alien_0['x_position']}\")\n",
    "\n",
    "# 向右移动外星人\n",
    "# 根据当前速度确定将外星人向右移动多远\n",
    "if alien_0['speed'] == 'slow':\n",
    "    x_increment = 1\n",
    "elif alien_0['speed'] == 'medium':\n",
    "    x_increment = 2\n",
    "else:\n",
    "    # 这个外星人的移动速度肯定很快\n",
    "    x_increment = 3\n",
    "\n",
    "# 新位置为旧位置加上移动距离\n",
    "alien_0['x_position'] = alien_0['x_position'] + x_increment\n",
    "\n",
    "print(f\"New position: {alien_0['x_position']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先定义一个外星人，其中包含初始 `x` 坐标和 `y` 坐标，还有速度 `'medium'`。出于简化考虑，这里省略了颜色和分数，但即便包含这些键值对，这个示例的工作原理也不会有任何变化。我们还打印了 `x_position` 的初始值，旨在让用户知道这个外星人向右移动了多远。\n",
    "\n",
    "❶处使用了一个 `if-elif-else` 语句来确定外星人应该向右移动多远，并将这个值赋给变量 `x_increment`。如果外星人的速度为 `'slow'`，它将向右移动 1 个单位；如果速度为 `'medium'`，将向右移动 2 个单位；如果速度更快，将向右移动 3 个单位。确定移动量后，将其与 `x_position` 的当前值相加 (见❷)，再将结果关联到字典中的键 `x_position`。\n",
    "\n",
    "因为这是一个速度中等的外星人，所以其位置将向右移动 2 个单位：\n",
    "```\n",
    "Original position: 0\n",
    "New position: 2\n",
    "```\n",
    "这种技巧很棒：通过修改字典中一个字典的值，可改变外星人的行为。例如，要将这个速度中等的外星人变成速度很快的外星人，可添加如下代码行：\n",
    "```python\n",
    "alien_0['speed'] = 'fast'\n",
    "```\n",
    "这样，再次运行这些代码时，`if-elif-else` 语句将一个更大的值赋给变量 `x_increment`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.5 删除键值对"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于字典中不再需要的信息，可使用 `del` 语句将相应的键值对彻底删除。在使用 `del` 语句时，必须指定字典名和要删除的键。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例如，下面的代码从字典 `alien_0` 中删除键 `'points'` 及其值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alien.py\n",
    "alien_0 = {'color': 'green', 'points': 5}\n",
    "print(alien_0)\n",
    "\n",
    "# ●\n",
    "del alien_0['points']\n",
    "print(alien_0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "●处的 `del` 语句让 Python 将键 `'points'` 从字典 `alien_0` 中删除，同时删除与这个键关联的值。输出表明，键 `'points'` 及其值 `5` 已被从字典中删除，但其他键值对未受影响：\n",
    "```\n",
    "{'color': 'green', 'points': 5}\n",
    "{'color': 'green'}\n",
    "```\n",
    "**注意：** 删除的键值对永远消失了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.6 由类似对象组成的字典"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在前面的示例中，字典存储的是一个对象 (游戏中的一个外星人) 的多种信息，但也可以使用字典来存储众多对象的同一种信息。假设你要调查很多人，询问他们喜欢的编程语言，可使用一个字典来存储这种简单的调查结果，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如你所见，我们将一个较大的字典放在了多行中。每个键都是一个被调查者的名字，而值都是被调查者喜欢的语言。当确定需要使用多行来定义字典时，先在输入左花括号后按回车键，再在下一行缩进 4 个空格，指定第一个键值对，并在它后面加上一个逗号。此后按回车键，文本编辑器将自动缩进后续键值对，且缩进量与第一个键值对相同。\n",
    "\n",
    "定义好字典后，在最后一个键值对的下一行添加一个右花括号，并且也缩进 4 个空格。一种不错的做法是，在最后一个键值对后面也加上逗号，为以后添加键值对做好准备。\n",
    "\n",
    "> **注意：** 对于较长的列表和字典，大多数编辑器提供了以类似方式设置格式的功能。对于较长的字典，还有其他一些可行的格式设置方式，因此在你的编辑器或其他源代码中，你可能会看到稍微不同的格式设置方式。\n",
    "\n",
    "给定被调查者的名字，可使用这个字典轻松地了解他喜欢的语言："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "language = favorite_languages['sarah'].title()\n",
    "print(f\"Sarah's favorite language is {language}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了解 Sarah 喜欢的语言，我们使用如下代码： `favorite_languages['sarah']`\n",
    "\n",
    "在❶处，使用这种语法获取 Sarah 喜欢的语言，并将其转换为首字母大写的赋给变量 `language`。创建这个新变量，使得函数调用 `print()` 整洁得多。输出指出了 Sarah 喜欢的语言：\n",
    "```\n",
    "Sarah's favorite language is C.\n",
    "```\n",
    "这种语法可用来从字典中获取任何人喜欢的语言。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.7 使用 get() 来访问值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用放在方括号内的键从字典中获取感兴趣的值，可能会引发问题：如果指定的键不存在，就将出错。\n",
    "\n",
    "如果你要求获取外星人的分数，而这个外星人没有分数，结果将如何呢？下面来看一看："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# alien_no_points.py\n",
    "alien_0 = {'color': 'green', 'speed': 'slow'}\n",
    "# print(alien_0['points']) # This line will cause a KeyError"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这将导致 Python 显示 traceback，指出存在键值错误 (KeyError)：\n",
    "```\n",
    "Traceback (most recent call last):\n",
    "  File \"alien_no_points.py\", line 2, in <module>\n",
    "    print(alien_0['points'])\n",
    "KeyError: 'points'\n",
    "```\n",
    "第 10 讲将详细介绍如何处理类似的错误。就字典而言，为避免这样的错误，可使用 `get()` 方法在指定的键不存在时返回一个默认值。`get()` 方法的第一个参数用于指定键，是必不可少的；第二个参数为指定的键不存在时要返回的值，是可选的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alien_0 = {'color': 'green', 'speed': 'slow'}\n",
    "\n",
    "point_value = alien_0.get('points', 'No point value assigned.')\n",
    "print(point_value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果字典中有键 `'points'`，将获得与之关联的值；如果没有，将获得指定的默认值。虽然这里没有键 `'points'`，但是我们将获得一条清晰的消息，不会引发错误：\n",
    "```\n",
    "No point value assigned.\n",
    "```\n",
    "如果指定的键有可能不存在，应考虑使用 `get()` 方法，而不要使用方括号表示法。\n",
    "\n",
    "> **注意：** 在调用 `get()` 时，如果没有指定第二个参数且指定的键不存在，Python 将返回 `None`，这个特殊的值表示没有相应的值。这并非错误：`None` 只是一个表示所需值不存在的特殊值，第 8 讲将介绍它的其他用途。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 动手试一试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**练习 1：人**\n",
    "使用一个字典来存储一个人的信息，包括名、姓、年龄和居住的城市。该字典应包含键 `first_name`、`last_name`、`age` 和 `city`。将存储在该字典中的每项信息都打印出来。\n",
    "\n",
    "---\n",
    "**练习 2：喜欢的数**\n",
    "使用一个字典来存储一些人喜欢的数。请想出 5 个人的名字，并将这些名字用作字典中的键。再想出每个人喜欢的一个数，并将这些数作为值存储在字典中。打印每个人的名字和喜欢的数。为了让这个程序更有趣，通过询问朋友确保数据是真实的。\n",
    "\n",
    "---\n",
    "**练习 3：词汇表 1**\n",
    "Python 字典可用于模拟现实生活中的字典。为避免混淆，我们将后者称为词汇表。\n",
    "*   想出你在前面学过的 5 个编程术语，将它们用作词汇表中的键，并将它们的含义作为值存储在词汇表中。\n",
    "*   以整洁的方式打印每个术语及其含义。为此，既可以先打印术语，在它后面加上一个冒号，再打印其含义；也可以先在一行里打印术语，再使用换行符 (`\\n`) 插入一个空行，然后在下一行以缩进的方式打印其含义。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3 遍历字典"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个 Python 字典可能只包含几个键值对，也可能包含数百万个键值对。鉴于字典可能包含大量数据，Python 支持对字典进行遍历。字典可用于以各种方式存储信息，因此有多种遍历方式：既可遍历字典的所有键值对，也可只遍历键或值。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 遍历所有的键值对"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在探索各种遍历方法前，先来看一个新字典，它用于存储有关网站用户的信息。这个字典存储一个用户的用户名、名和姓："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# user.py\n",
    "user_0 = {\n",
    "    'username': 'efermi',\n",
    "    'first': 'enrico',\n",
    "    'last': 'fermi',\n",
    "    }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "利用本讲前面介绍过的知识，可访问 `user_0` 的任意一项信息，但如果要获悉该用户字典中的所有信息，该怎么办？可使用 `for` 循环来遍历这个字典："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_0 = {\n",
    "    'username': 'efermi',\n",
    "    'first': 'enrico',\n",
    "    'last': 'fermi',\n",
    "    }\n",
    "\n",
    "for key, value in user_0.items():\n",
    "    print(f\"\\nKey: {key}\")\n",
    "    print(f\"Value: {value}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要编写遍历字典的 `for` 循环，可声明两个变量，分别用于存储键值对中的键和值。这两个变量可以使用任意名称。下面的代码使用了简单的变量名，这完全可行：\n",
    "`for k, v in user_0.items()`\n",
    "\n",
    "`for` 语句的第二部分包含字典名和方法 `items()`，这个方法返回一个键值对列表。接下来，`for` 循环依次将每个键值对赋给指定的两个变量。在这个示例中，使用这两个变量来打印每个键和与它关联的值。第一个函数调用 `print()` 中的 `\"\\n\"` 确保在输出每个键值对前插入一个空行：\n",
    "```\n",
    "Key: username\n",
    "Value: efermi\n",
    "\n",
    "Key: first\n",
    "Value: enrico\n",
    "\n",
    "Key: last\n",
    "Value: fermi\n",
    "```\n",
    "在 2.6 节的示例 `favorite_languages.py` 中，字典存储的是不同的人的同一种信息。对于类似这样的字典，遍历所有的键值对很合适。如果遍历字典 `favorite_languages`，将得到其中每个人的姓名和他喜欢的编程语言。由于该字典中的键都是人名，而值都是语言，因此在循环中使用变量 `name` 和 `language`，而不是 `key` 和 `value`。这让人更容易明白循环的作用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# favorite_languages.py\n",
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "for name, language in favorite_languages.items():\n",
    "    print(f\"{name.title()}'s favorite language is {language.title()}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这些代码让 Python 遍历字典中的每个键值对，并将键赋给变量 `name`，将值赋给变量 `language`。这些描述性名称让人能够非常轻松地明白函数调用 `print()` 是做什么的。\n",
    "\n",
    "仅用几行代码，就将全部调查结果显示出来了：\n",
    "```\n",
    "Jen's favorite language is Python.\n",
    "Sarah's favorite language is C.\n",
    "Edward's favorite language is Rust.\n",
    "Phil's favorite language is Python.\n",
    "```\n",
    "即便字典存储了上千乃至上百万人的调查结果，这种循环也管用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2 遍历字典中的所有键"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在不需要使用字典中的值时，方法 `keys()` 很有用。下面来遍历字典 `favorite_languages`，并将每个被调查者的名字都打印出来："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "for name in favorite_languages.keys():\n",
    "    print(name.title())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个 `for` 循环让 Python 提取字典 `favorite_languages` 中的所有键，并依次将它们赋给变量 `name`。输出列出了每个被调查者的名字：\n",
    "```\n",
    "Jen\n",
    "Sarah\n",
    "Edward\n",
    "Phil\n",
    "```\n",
    "在遍历字典时，会默认遍历所有的键。因此，如果将上述代码中的：\n",
    "`for name in favorite_languages.keys():`\n",
    "\n",
    "替换为：\n",
    "`for name in favorite_languages:`\n",
    "\n",
    "输出将不变。\n",
    "\n",
    "如果显式地使用 `keys()` 方法能让代码更容易理解，就可以选择这样做，但如果你愿意，也可以省略它。\n",
    "\n",
    "在这种循环中，可使用当前的键来访问与之关联的值。下面来打印两条消息，指出两个朋友喜欢的编程语言。我们像前面一样遍历字典中的名字，但在名字为指定朋友的名字时，打印一条消息，指出其喜欢的语言："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "friends = ['phil', 'sarah']\n",
    "for name in favorite_languages.keys():\n",
    "    print(f\"Hi {name.title()}.\")\n",
    "    \n",
    "    if name in friends:\n",
    "        language = favorite_languages[name].title()\n",
    "        print(f\"\\t{name.title()}, I see you love {language}!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先创建一个列表，其中包含要收到打印消息的朋友。在循环中，打印每个人的名字，并检查当前的名字是否在列表 `friends` 中（见❶）。如果是，就打印一句特殊的问候语，其中包含这个朋友喜欢的语言。为获悉这个朋友喜欢的语言，这里使用了字典名，并将变量 `name` 的当前值作为键（见❷）。\n",
    "\n",
    "每个人的名字都会被打印出来，但只对朋友打印特殊消息：\n",
    "```\n",
    "Hi Jen.\n",
    "Hi Sarah.\n",
    "\\t\\tSarah, I see you love C!\n",
    "Hi Edward.\n",
    "Hi Phil.\n",
    "\\t\\tPhil, I see you love Python!\n",
    "```\n",
    "还可以使用 `keys()` 确定某个人是否接受了调查。下面的代码确定 Erin 是否接受了调查："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "if 'erin' not in favorite_languages.keys():\n",
    "    print(\"Erin, please take our poll!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`keys()` 方法并非只能用于遍历；实际上，它会返回一个列表，其中包含字典中的所有键。因此 `if` 语句只核实 `'erin'` 是否在这个列表中。因为她并不在这个列表中，所以打印一条消息，邀请她参加调查：\n",
    "```\n",
    "Erin, please take our poll!\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3 按特定的顺序遍历字典中的所有键"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "遍历字典时将按插入元素的顺序返回其中的元素，但是在一些情况下，你可能要按与此不同的顺序遍历字典。\n",
    "\n",
    "要以特定的顺序返回返回字典元素，一种办法是在 `for` 循环中对返回的键进行排序。为此，可使用 `sorted()` 函数来获得按特定顺序排列的键列表的副本："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "for name in sorted(favorite_languages.keys()):\n",
    "    print(f\"{name.title()}, thank you for taking the poll.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这条 `for` 语句类似于其他 `for` 语句，但对方法 `dictionary.keys()` 的结果调用了 `sorted()` 函数。这让 Python 获取字典中的所有键，并在遍历前对这个列表进行排序。输出表明，按字母顺序列出了所有被调查者的名字：\n",
    "```\n",
    "Edward, thank you for taking the poll.\n",
    "Jen, thank you for taking the poll.\n",
    "Phil, thank you for taking the poll.\n",
    "Sarah, thank you for taking the poll.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4 遍历字典中的所有值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果你感兴趣的是字典包含的值，可使用 `values()` 方法。它会返回一个值列表，不包含任何键。如果想获得一个只包含被调查者选择的各种语言，而不包含任何被调查者名字的列表，可以这样做："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "print(\"The following languages have been mentioned:\")\n",
    "for language in favorite_languages.values():\n",
    "    print(language.title())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这条 `for` 语句提取字典中的每个值，并将它们依次赋给变量 `language`。通过打印这些值，就获得了一个包含被调查者选择的各种语言的列表：\n",
    "```\n",
    "The following languages have been mentioned:\n",
    "Python\n",
    "C\n",
    "Rust\n",
    "Python\n",
    "```\n",
    "这种做法提取字典中所有的值，而没有考虑值是否有重复。当涉及的值很少时，这也许不是问题，但如果被调查者很多，最终的列表可能包含大量的重复项。为剔除重复项，可使用集合 (`set`)。集合中的每个元素都必须是独一无二的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_languages = {\n",
    "    'jen': 'python',\n",
    "    'sarah': 'c',\n",
    "    'edward': 'rust',\n",
    "    'phil': 'python',\n",
    "    }\n",
    "\n",
    "print(\"The following languages have been mentioned:\")\n",
    "for language in set(favorite_languages.values()):\n",
    "    print(language.title())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过将包含重复元素的列表传入 `set()`，可让 Python 找出列表中独一无二的元素，并使用这些元素来创建一个集合。这里使用 `set()` 来提取 `favorite_languages.values()` 中不同的语言。\n",
    "\n",
    "结果是一个没有重复元素的列表，其中列出了被调查者提及的所有语言：\n",
    "```\n",
    "The following languages have been mentioned:\n",
    "Python\n",
    "C\n",
    "Rust\n",
    "```\n",
    "随着学习的深入，你会经常发现 Python 内置的功能可帮助你以希望的方式处理数据。\n",
    "\n",
    "> **注意：** 可以使用一对花括号直接创建集合，并在其中用逗号分隔元素：\n",
    "> ```python\n",
    "> languages = {'python', 'rust', 'python', 'c'}\n",
    "> languages\n",
    "> {'rust', 'python', 'c'}\n",
    "> ```\n",
    "> 集合和字典很容易混淆，因为它们都是用一对花括号定义的。当花括号内没有键值对时，定义的很可能是集合。不同于列表和字典，集合不会以特定的顺序存储元素。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 动手试一试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**练习 4：词汇表 2**\n",
    "现在你知道了如何遍历字典，请整理你为练习 3 编写的代码，将其中的一系列函数调用 `print()` 替换为一个遍历字典中键和值的循环。确保该循环无误后，再在词汇表中添加 5 个 Python 术语。当你再次运行这个程序时，这些新术语及其含义将自动包含在输出中。\n",
    "\n",
    "---\n",
    "**练习 5：河流**\n",
    "创建一个字典，在其中存储三条大河及其流经的国家。例如，一个键值对可能是 `'nile': 'egypt'`。\n",
    "*   使用循环为每条河流打印一条消息，如下所示：`The Nile runs through Egypt.`\n",
    "*   使用循环将该字典中每条河流的名字打印出来。\n",
    "*   使用循环将该字典包含的每个国家的名字打印出来。\n",
    "\n",
    "---\n",
    "**练习 6：调查**\n",
    "在 3.1 节编写的程序 `favorite_languages.py` 中执行以下操作。\n",
    "*   创建一个应该会接受调查的人的名单，其中有些人已在字典中，而其他人不在字典中。\n",
    "*   遍历这个名单。对于已参与调查的人，打印一条消息表示感谢；对于还未参与调查的人，打印一条邀请参加调查的消息。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4 嵌套"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有时候，需要将多个字典存储在列表中或将列表作为值存储在字典中，这称为**嵌套**。你可以在列表中嵌套字典，在字典中嵌套列表，甚至在字典中嵌套字典。正如下面的示例将演示的，嵌套是一个强大的功能。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.1 字典列表"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "字典 `alien_0` 包含一个外星人的各种信息，但无法存储第二个外星人的信息，更别说屏幕上全部外星人的信息了。如何管理成群结队的外星人呢？一种办法是创建一个外星人列表。其中每个外星人都是一个字典，包含有关该外星人的各种信息。例如，下面的代码创建一个包含三个外星人的列表："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# aliens.py\n",
    "alien_0 = {'color': 'green', 'points': 5}\n",
    "alien_1 = {'color': 'yellow', 'points': 10}\n",
    "alien_2 = {'color': 'red', 'points': 15}\n",
    "\n",
    "aliens = [alien_0, alien_1, alien_2]\n",
    "\n",
    "for alien in aliens:\n",
    "    print(alien)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先创建三个字典，其中每个字典都表示一个外星人。然后在❶处，将这些字典都存储到一个名为 `aliens` 的列表中。最后，遍历这个列表，将每个外星人都打印出来：\n",
    "```\n",
    "{'color': 'green', 'points': 5}\n",
    "{'color': 'yellow', 'points': 10}\n",
    "{'color': 'red', 'points': 15}\n",
    "```\n",
    "更符合现实的情形是，外星人不止三个，而且每个外星人都是用代码自动生成的。在下面的示例中，使用 `range()` 生成了 30 个外星人："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个用于存储外星人的空列表\n",
    "aliens = []\n",
    "\n",
    "# 创建 30 个绿色的外星人\n",
    "for alien_number in range(30):\n",
    "    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "    aliens.append(new_alien)\n",
    "\n",
    "# 显示前 5 个外星人\n",
    "for alien in aliens[:5]:\n",
    "    print(alien)\n",
    "print(\"...\")\n",
    "\n",
    "# 显示创建了多少个外星人\n",
    "print(f\"Total number of aliens: {len(aliens)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个示例中，首先创建了一个空列表，用于存储接下来将创建的所有外星人。在❶处，`range()` 函数返回一个数字序列，告诉 Python 要重复这个循环多少次。每次执行这个循环时，都创建一个外星人 (见❷)，并将其附加到列表 `aliens` 末尾 (见❸)。在❹处，使用一个切片来打印前 5 个外星人。最后，打印列表的长度，以证明确实创建了 30 个外星人：\n",
    "```\n",
    "{'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "{'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "{'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "{'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "{'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "...\n",
    "Total number of aliens: 30\n",
    "```\n",
    "这些外星人都具有相同的特征，但在 Python 看来，每个外星人都是独立的，这让我们能够独立地修改每个外星人。\n",
    "\n",
    "在什么情况下需要处理成群结队的外星人呢？想象一下，随着游戏的进行，有些外星人会变色且加快移动速度。在必要时，可使用 `for` 循环和 `if` 语句来修改某些外星人的颜色。例如，要将前三个外星人修改为黄色、速度中等且值 10 分，可这样做："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个空列表\n",
    "aliens = []\n",
    "# 创建 30 个绿色外星人\n",
    "for alien_number in range(30):\n",
    "    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "    aliens.append(new_alien)\n",
    "\n",
    "for alien in aliens[:3]:\n",
    "    if alien['color'] == 'green':\n",
    "        alien['color'] = 'yellow'\n",
    "        alien['speed'] = 'medium'\n",
    "        alien['points'] = 10\n",
    "\n",
    "# 显示前 5 个外星人\n",
    "for alien in aliens[:5]:\n",
    "    print(alien)\n",
    "print(\"...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "鉴于要修改前三个外星人，我们遍历一个只包含这些外星人的切片。当前，所有外星人都是绿色的，但情况并非总是如此，因此可以编写一条 `if` 语句来确保只修改绿色外星人。如果外星人是绿色的，就将其颜色改为 `'yellow'`，将其速度改为 `'medium'`，并将其分数改为 `10`，如下面的输出所示：\n",
    "```\n",
    "{'color': 'yellow', 'points': 10, 'speed': 'medium'}\n",
    "{'color': 'yellow', 'points': 10, 'speed': 'medium'}\n",
    "{'color': 'yellow', 'points': 10, 'speed': 'medium'}\n",
    "{'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "{'color': 'green', 'points': 5, 'speed': 'slow'}\n",
    "...\n",
    "```\n",
    "可进一步扩展这个循环，在其中添加一个 `elif` 代码块，将黄色外星人改为移动速度快且值 15 分的红色外星人，如下所示 (这里只列出了循环，没有列出整个程序)：\n",
    "```python\n",
    "for alien in aliens[0:3]:\n",
    "    if alien['color'] == 'green':\n",
    "        alien['color'] = 'yellow'\n",
    "        alien['speed'] = 'medium'\n",
    "        alien['points'] = 10\n",
    "    elif alien['color'] == 'yellow':\n",
    "        alien['color'] = 'red'\n",
    "        alien['speed'] = 'fast'\n",
    "        alien['points'] = 15\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2 在字典中存储列表"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有时候，需要将列表存储在字典中，而不是将字典存储在列表中。例如，如何描述顾客点的比萨呢？如果使用列表，只能存储要添加的比萨配料；但如果使用字典，其中配料列表就只是用来描述比萨的一个方面。\n",
    "\n",
    "在下面的示例中，存储了比萨两个方面的信息：外皮类型和配料列表。配料列表是与键 `'toppings'` 关联的值。要访问该列表，我们使用字典名和键 `'toppings'`，就像访问字典中的其他值一样。这将返回一个配料列表，而不是单个值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# pizza.py\n",
    "# 存储顾客所点比萨的信息\n",
    "pizza = {\n",
    "    'crust': 'thick',\n",
    "    'toppings': ['mushrooms', 'extra cheese'],\n",
    "    }\n",
    "\n",
    "# 概述顾客点的比萨\n",
    "print(f\"You ordered a {pizza['crust']}-crust pizza \"\n",
    "      \"with the following toppings:\")\n",
    "\n",
    "for topping in pizza['toppings']:\n",
    "    print(f\"\\t{topping}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先创建一个字典，其中存储了顾客所点比萨的信息。在这个字典中，一个键是 `'crust'`，与之关联的值是字符串 `'thick'`；另一个键是 `'toppings'`，与之关联的是一个列表，其中存储了顾客要求添加的所有配料。制作前，我们概述了顾客点的比萨 (见❶)。当函数调用 `print()` 中的字符串串很长，需要分成多行书写时，可以在合适的位置分行，在每行末尾都加上引号，并且除第一行外的其他各行，都在行首加上引号并缩进。这样，Python 将自动合并括号内的所有字符串。为打印配料，编写一个 `for` 循环 (见❷)。为了访问配料列表，使用键 `'toppings'`，这样 Python 将从字典中提取配料列表。\n",
    "\n",
    "下面的输出概述了要制作的比萨：\n",
    "```\n",
    "You ordered a thick-crust pizza with the following toppings:\n",
    "\\t\\tmushrooms\n",
    "\\t\\textra cheese\n",
    "```\n",
    "每当需要在字典中将一个键关联到多个值时，都可以在字典中嵌套一个列表。在本讲前面有关喜欢的编程语言的示例中，如果将每个人的回答都存储在列表中，被调查者就可以选择多种喜欢的语言。在这种情况下，当我们遍历字典时，与每个被调查者关联的都是一个语言列表，而不是一种语言。因此，在遍历该字典的 `for` 循环中，需要再使用一个 `for` 循环来遍历与被调查者关联的语言列表："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# favorite_languages.py\n",
    "favorite_languages = {\n",
    "    'jen': ['python', 'rust'],\n",
    "    'sarah': ['c'],\n",
    "    'edward': ['rust', 'go'],\n",
    "    'phil': ['python', 'haskell'],\n",
    "    }\n",
    "\n",
    "for name, languages in favorite_languages.items():\n",
    "    print(f\"\\n{name.title()}'s favorite languages are:\")\n",
    "    for language in languages:\n",
    "        print(f\"\\t{language.title()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这里，与每个名字关联的值都是一个列表。请注意，有些人喜欢的语言只有一种，而有些人喜欢的不止一种。在遍历字典时 (见❶)，使用变量 `languages` 来依次存储字典中的每个值，因为我知道这些值都是列表。在遍历字典的主循环中，使用另一个 `for` 循环 (见❷)。现在，使用另一个 `for` 循环来遍历每个人喜欢的语言列表。现在，每个人想列出多少种喜欢的语言都可以：\n",
    "```\n",
    "Jen's favorite languages are:\n",
    "\\tPython\n",
    "\\tRust\n",
    "\n",
    "Sarah's favorite languages are:\n",
    "\\tC\n",
    "\n",
    "Edward's favorite languages are:\n",
    "\\tRust\n",
    "\\tGo\n",
    "\n",
    "Phil's favorite languages are:\n",
    "\\tPython\n",
    "\\tHaskell\n",
    "```\n",
    "为了进一步改进这个程序，可在遍历字典的 `for` 循环开头添加一条 `if` 语句，通过查看 `len(languages)` 的值来确定当前被调查者喜欢的语言是否有一种。如果他喜欢的语言不止一种，就像以前一样显示输出；如果只有一种，就相应地修改输出的措辞，如显示 `Sarah's favorite language is C`。\n",
    "\n",
    "> **注意：** 列表和字典的嵌套层级不应太多。如果嵌套层级比前面的示例多很多，很可能有更简单的解决方案。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.3 在字典中存储字典"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以在字典中嵌套字典，但这样做可能会让代码很快变得非常复杂。如果有一个网站有多名用户，可将用户名作为键，然后将每个用户的信息存储在一个字典中，并将该字典作为与用户名关联的值。在下面的程序中，存储每个用户的三项信息：名、姓和居住地。为了访问这些信息，我们遍历所有的用户名，并访问与每个用户名关联的信息字典："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# many_users.py\n",
    "\n",
    "users = {\n",
    "    'aeinstein': {\n",
    "        'first': 'albert',\n",
    "        'last': 'einstein',\n",
    "        'location': 'princeton',\n",
    "        },\n",
    "    'mcurie': {\n",
    "        'first': 'marie',\n",
    "        'last': 'curie',\n",
    "        'location': 'paris',\n",
    "        },\n",
    "    }\n",
    "\n",
    "for username, user_info in users.items():\n",
    "    print(f\"\\nUsername: {username}\")\n",
    "    full_name = f\"{user_info['first']} {user_info['last']}\"\n",
    "    location = user_info['location']\n",
    "    \n",
    "    print(f\"\\tFull name: {full_name.title()}\")\n",
    "    print(f\"\\tLocation: {location.title()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先定义一个名为 `users` 的字典，其中包含两个键：用户名 `'aeinstein'` 和 `'mcurie'`。与每个键关联的值都是一个字典，其中包含用户的名、姓和居住地。然后，遍历字典 `users` (见❶)，让 Python 依次将每个键赋给变量 `username`，并依次将与当前键相关联的字典赋给变量 `user_info`。在循环内部，将用户名打印出来 (见❷)。\n",
    "\n",
    "接下来，开始访问内部的字典 (见❸)。变量 `user_info` 包含用户信息字典，而该字典包含三个键：`'first'`、`'last'` 和 `'location'`。都使用这些键来生成整洁的姓名和居住地，然后打印有关用户的简要信息 (见❹)：\n",
    "```\n",
    "Username: aeinstein\n",
    "\\t\\tFull name: Albert Einstein\n",
    "\\t\\tLocation: Princeton\n",
    "\n",
    "Username: mcurie\n",
    "\\t\\tFull name: Marie Curie\n",
    "\\t\\tLocation: Paris\n",
    "```\n",
    "请注意，表示每个用户的字典都有相同的结构，虽然 Python 并没有这样的要求，但这使得嵌套的字典处理起来更容易。倘若表示每个用户的字典都包含不同的键，`for` 循环内部的代码将更复杂。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 动手试一试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**练习 7：人们**\n",
    "在为练习 1 编写的程序中，再创建两个表示人的字典，然后将这三个字典都存储在一个名为 `people` 的列表中。遍历这个列表，将其中每个人的所有信息都打印出来。\n",
    "\n",
    "---\n",
    "**练习 8：宠物**\n",
    "创建多个表示宠物的字典，每个字典都包含宠物的类型及其主人的名字。将这些字典存储在一个名为 `pets` 的列表中，再遍历该列表，并将有关每个宠物的所有信息都打印出来。\n",
    "\n",
    "---\n",
    "**练习 9：喜欢的地方**\n",
    "创建一个名为 `favorite_places` 的字典。在这个字典中，将三个人的名字用作键，并存储每个人喜欢的 1~3 个地方。为让这个练习更有趣些，让一些朋友说出他们喜欢的几个地方。遍历这个字典，并将其中每个人的名字及其喜欢的地方打印出来。\n",
    "\n",
    "---\n",
    "**练习 10：喜欢的数 2**\n",
    "修改为练习 2 编写的程序，让每个人都可以有多个喜欢的数，然后将每个人的名字及其喜欢的数打印出来。\n",
    "\n",
    "---\n",
    "**练习 11：城市**\n",
    "创建一个名为 `cities` 的字典，将三个城市名用作键。对于每座城市，都创建一个字典，并在其中包含该城市所属的国家、人口约数以及一个有关该城市的事实。表示城市的字典应包含 `country`、`population` 和 `fact` 等键。将每座城市的名字以及相关信息都打印出来。\n",
    "\n",
    "---\n",
    "**练习 12：扩展**\n",
    "本讲的示例足够复杂，能以很多方式进行扩展。请对本讲的一个示例进行扩展：添加键和值，调整程序要解决的问题，或改进输出的格式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在本讲中，你首先学习了如何定义字典，以及如何使用存储在字典中的信息。然后学习了如何访问和修改字典中的元素，以及如何遍历字典中的所有信息。接着学习了如何遍历字典中的所有键值对、所有的键和所有的值。你还学习了如何在列表中嵌套字典，如何在字典中嵌套列表，以及如何在字典中嵌套字典。\n",
    "\n",
    "在下一讲中，你将学习 `while` 循环，以及如何从用户那里获取输入。这是激动人心的一讲，让你知道如何将程序变成可交互的，能够对用户的输入做出响应。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
